#include "my_wifi.h"
#include <string.h>
#include <esp_log.h>
#include <auto_json.h>

#ifndef MAC2STR
#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
#endif

#define Debug 0

static const char *TAG_AP = "WiFi SoftAP";
static const char *TAG_STA = "WiFi Sta";

esp_netif_t *WiFi::staInit()
{
    esp_netif_t *esp_netif_sta = esp_netif_create_default_wifi_sta();
    // regist wifi config infos
    wifi_config_t wifi_config_infos = {
        .sta = {
            //.ssid = "",
            //.password = "",
            /* Authmode threshold resets to WPA2 as default if password matches WPA2 standards (password len => 8).
             * If you want to connect the device to deprecated WEP/WPA networks, Please set the threshold value
             * to WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK and set the password with length and format matching to
             * WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK standards.
             */
            .threshold = {.authmode = WIFI_AUTH_WPA2_PSK},
            .sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
            .sae_h2e_identifier = "",
        },
    };
    memcpy(wifi_config_infos.sta.ssid, ssid.c_str(), ssid.length());
    memcpy(wifi_config_infos.sta.password, password.c_str(), password.length());

    if (password.length() == 0)
    {
        wifi_config_infos.sta.threshold.authmode = WIFI_AUTH_OPEN;
    }

    // ESP_LOGI(TAG_STA, "SSID:%s PWD:%s", wifi_config_infos.sta.ssid, wifi_config_infos.sta.password);

    esp_wifi_set_config(WIFI_IF_STA, &wifi_config_infos);
    return esp_netif_sta;
}
esp_netif_t *WiFi::apInit()
{
    esp_netif_t *esp_netif_ap = esp_netif_create_default_wifi_ap();

    wifi_config_t wifi_ap_config = {
        .ap = {
            //.ssid = EXAMPLE_ESP_WIFI_AP_SSID,
            //.ssid_len = strlen(EXAMPLE_ESP_WIFI_AP_SSID),
            .channel = 6,
            // .password = EXAMPLE_ESP_WIFI_AP_PASSWD,
            .authmode = WIFI_AUTH_WPA2_PSK,
            .max_connection = 3,
            .pmf_cfg = {
                .required = false,
            },
        },
    };
    memcpy(wifi_ap_config.ap.ssid, ap_ssid.c_str(), ap_ssid.length());
    memcpy(wifi_ap_config.ap.password, ap_password.c_str(), ap_password.length());
    wifi_ap_config.ap.ssid_len = ap_password.length();
    ESP_LOGW(TAG_AP, "SSID:%s PWD:%s", wifi_ap_config.ap.ssid, wifi_ap_config.ap.password);

    // set authmode to open
    if (ap_password.length() < 8)
        wifi_ap_config.ap.authmode = WIFI_AUTH_OPEN;

    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_ap_config));

    return esp_netif_ap;
}
esp_err_t WiFi::start()
{
    auto wifiEventCall = [](void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
    {
        // get the wifi handler object
        WiFi *self = (WiFi *)event_handler_arg;
        if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STACONNECTED)
        {
            wifi_event_ap_staconnected_t *event = (wifi_event_ap_staconnected_t *)event_data;
#if Debug
            ESP_LOGI(TAG_AP, "Station " MACSTR " joined, AID=%d",
                     MAC2STR(event->mac), event->aid);
#endif
        }
        else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STADISCONNECTED)
        {
            wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t *)event_data;
#if Debug
            ESP_LOGI(TAG_AP, "Station " MACSTR " left, AID=%d, reason:%d",
                     MAC2STR(event->mac), event->aid, event->reason);
#endif
        }
        else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
        {
// esp_wifi_connect();
#if Debug
            ESP_LOGI(TAG_STA, "Station started");
#endif
        }
        else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED)
        {
#if Debug
            ESP_LOGI(TAG_STA, "Station connected to ssid:%s", self->ssid.c_str());
#endif
        }
        else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
        {
            ESP_LOGW(TAG_STA, "Disconnected from ssid:%s", self->ssid.c_str());
            if (self->status_cb != nullptr)
                self->status_cb(wifi_status_t::WIFI_DISCONNECTED);
            self->status = wifi_status_t::WIFI_DISCONNECTED;
        }

        // get the ip infos
        if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
        {
            self->status = wifi_status_t::WIFI_CONNECTED;

            ip_event_got_ip_t *ip_infos = (ip_event_got_ip_t *)event_data;
            esp_ip4_addr_t ip = ip_infos->ip_info.ip;

            char ip_str[32];
            snprintf(ip_str, sizeof(ip_str), IPSTR, IP2STR(&ip));
            self->sta_ip = std::string(ip_str);

            if (self->status_cb != nullptr)
                self->status_cb(wifi_status_t::WIFI_CONNECTED);
        }
    };

    esp_event_handler_instance_t event_handler;
    esp_err_t ret = esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        wifiEventCall,
                                                        this,
                                                        &event_handler);
    esp_event_handler_instance_t ip_event_handler;
    esp_event_handler_instance_register(IP_EVENT,
                                        IP_EVENT_STA_GOT_IP,
                                        wifiEventCall,
                                        this,
                                        &ip_event_handler);

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    esp_wifi_init(&cfg);

    // esp_wifi_set_mode(WIFI_MODE_APSTA);
    // esp_netif_t *sta = staInit();
    // esp_netif_t *ap = apInit();

    return ESP_OK;
}

esp_err_t WiFi::connect()
{
    status = wifi_status_t::WIFI_DISCONNECTED;
    return esp_wifi_connect();
}
esp_err_t WiFi::connect(const std::string &ssid, const std::string &password)
{
    if (status == wifi_status_t::WIFI_CONNECTED)
        return ESP_OK;

    this->ssid = ssid;
    this->password = password;

    wifi_config_t wifi_config_infos = {
        .sta = {
            //.ssid = "",
            //.password = "",
            /* Authmode threshold resets to WPA2 as default if password matches WPA2 standards (password len => 8).
             * If you want to connect the device to deprecated WEP/WPA networks, Please set the threshold value
             * to WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK and set the password with length and format matching to
             * WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK standards.
             */
            .threshold = {.authmode = WIFI_AUTH_WPA2_PSK},
            .sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
            .sae_h2e_identifier = "",
        },
    };
    memcpy(wifi_config_infos.sta.ssid, ssid.c_str(), ssid.length());
    memcpy(wifi_config_infos.sta.password, password.c_str(), password.length());

    if (password.length() == 0)
    {
        wifi_config_infos.sta.threshold.authmode = WIFI_AUTH_OPEN;
    }

    esp_wifi_set_config(WIFI_IF_STA, &wifi_config_infos);

    status = wifi_status_t::WIFI_DISCONNECTED;
    return esp_wifi_connect();
}
void WiFi::setAp(const std::string &ap_ssid, const std::string &ap_password)
{
    this->ap_ssid = ap_ssid;
    this->ap_password = ap_password;
}

WiFi::WiFi(const std::string &ap_ssid, const std::string &ap_password, const std::string &ssid, const std::string &password)
{
    this->ap_ssid = ap_ssid;
    this->ap_password = ap_password;
    this->ssid = ssid;
    this->password = password;
}
std::string &WiFi::getIP()
{
    return sta_ip;
}
esp_err_t WiFi::setMode(wifi_mode_t mode)
{
    esp_wifi_stop();
    esp_wifi_set_mode(mode);
    if (mode == WIFI_MODE_STA && !sta_init)
    {
        sta_init = true;
        staInit();
    }
    else if (mode == WIFI_MODE_AP && !ap_init)
    {
        ap_init = true;
        apInit();
    }
    else if (mode == WIFI_MODE_APSTA)
    {
        if (!sta_init)
        {
            sta_init = true;
            staInit();
        }
        if (!ap_init)
        {
            ap_init = true;
            apInit();
        }
    }

    return esp_wifi_start();
}
wifi_mode_t WiFi::getMode()
{
  wifi_mode_t mode;
  esp_wifi_get_mode(&mode);
  return mode;
}
wifi_status_t WiFi::getStatus()
{
  return status;
}

std::string WiFi::getWiFiList(void)
{
    esp_wifi_disconnect();

    // 配置扫描参数
    wifi_scan_config_t scan_config = {
        .ssid = NULL,                       // 不指定 SSID，扫描所有
        .bssid = NULL,                      // 不指定 BSSID
        .channel = 0,                       // 扫描所有信道（0 表示全信道）
        .show_hidden = true,                // 显示隐藏网络
        .scan_type = WIFI_SCAN_TYPE_ACTIVE, // 主动扫描（更准确但耗时长）
        .scan_time = {
            .active = 300, // 每个信道主动扫描时间（ms）
            .passive = 100 // 每个信道被动扫描时间（ms，主动扫描时忽略）
        }};

    // 启动扫描（阻塞模式，超时时间 10 秒）
    ESP_ERROR_CHECK(esp_wifi_scan_start(&scan_config, true));

    // 获取扫描到的 AP 数量
    uint16_t ap_count = 0;
    ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count));
    // 申请内存存储 AP 信息
    wifi_ap_record_t *ap_records = (wifi_ap_record_t *)malloc(sizeof(wifi_ap_record_t) * ap_count);
    if (!ap_records)
    {
        ESP_LOGE(TAG_STA, "内存分配失败");
        return "";
    }

    // 获取 AP 详细信息
    ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_count, ap_records));

    AutoJson json(json_type_t::ARRAY_JSON);
    // 打印所有 AP 信息
    for (int i = 0; i < ap_count; i++)
    {
        json[i]["ssid"] = (char *)(ap_records[i].ssid);
        json[i]["rssi"] = ap_records[i].rssi;
    }

    // 释放内存
    free(ap_records);
    esp_wifi_scan_stop();

    return json.print();
}


esp_err_t WiFi::deinit()
{
    esp_wifi_disconnect();
    esp_wifi_stop();
    return esp_wifi_deinit();
}

WiFi::~WiFi()
{
    deinit();
}
